﻿using System;
using Castle.Core.Internal;
using FluentNHibernate.Automapping;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate.ByteCode.Castle;
using NHibernate.Cache;
using NHibernate.Cfg;
using ZenMoney.Core.DataAccess.Mappings;
using ZenMoney.Core.Entities;

namespace ZenMoney.Core.DataAccess.Config
{
    public class NHibernateConfig
    {
		private const string ConnectionStringName = "ZenMoneyConnectionString";

		public static Configuration Build()
		{
			var config = ConfigLoader.LoadFromFile();

			if (config == null)
			{
				config = BuildConfig();
				ConfigLoader.SaveToFile(config);
			}

			return config;
		}

        private static Configuration BuildConfig()
        {
            return Fluently.Configure()
                .Database(SetupDatabase)
                .Mappings(x => x.AutoMappings.Add(SetupAutoMappings))   
                .ProxyFactoryFactory<ProxyFactoryFactory>()            
				.Cache(x => x
					.UseQueryCache()
					.ProviderClass<HashtableCacheProvider>())
                .BuildConfiguration();
        }

        private static AutoPersistenceModel SetupAutoMappings()
        {
            return AutoMap                
                .AssemblyOf<BaseEntity>()
				.UseOverridesFromAssemblyOf<BaseEntity>()                
				.Conventions.AddFromAssemblyOf<BaseEntity>()
                .IgnoreBase<BaseEntity>()                
                .Where(ShouldMap)
                .OverrideAll(ShouldIgnoreProperty);                
        }

        private static IPersistenceConfigurer SetupDatabase()
        {
            return MsSqlConfiguration
                .MsSql2008
                .AdoNetBatchSize(50)
                .UseOuterJoin()
                .FormatSql()
                .ShowSql()				
                .ConnectionString(x => x
					.FromConnectionStringWithKey(ConnectionStringName));
        }

		// TODO: Should be moved to a separate class
        private static bool ShouldMap(Type type)
        {
            return typeof(BaseEntity).IsAssignableFrom(type);
        }

		// TODO: Should be moved to a separate class
        private static void ShouldIgnoreProperty(IPropertyIgnorer property)
        {
            property.IgnoreProperties(p => p.MemberInfo.HasAttribute<IgnoreAttribute>());
        }
    }
}
